home *** CD-ROM | disk | FTP | other *** search
- Subject: v13i090: Sun RPC, release 3.9, Part13/15
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Stephen X. Nahm <sxn@Sun.COM>
- Posting-number: Volume 13, Issue 90
- Archive-name: rpc3.9/part13
-
- #! /bin/sh
- # This is a shell archive. To extract, remove the header and type "sh filename"
- #
- cd doc
- echo x - xdr.nts.ms
- sed -e 's/^X//' > xdr.nts.ms <<'Funky_Stuff'
- X.\" @(#)xdr.nts.ms 1.2 87/11/09 3.9 RPCSRC
- X.de BT
- X.if \\n%=1 .tl ''- % -''
- X..
- X.ND
- X.\" prevent excess underlining in nroff
- X.if n .fp 2 R
- X.OH 'eXternal Data Representation: Sun Technical Notes''Page %'
- X.EH 'Page %''eXternal Data Representation: Sun Technical Notes'
- X.if \\n%=1 .bp
- X.SH
- \&eXternal Data Representation: Sun Technical Notes
- X.IX XDR "Sun technical notes"
- X.LP
- This chapter contains technical notes on Sun's implementation of the
- eXternal Data Representation (XDR) standard, a set of library routines
- that allow a C programmer to describe arbitrary data structures in a
- machine-independent fashion. For a formal specification of the XDR
- standard, see the
- \fIeXternal Data Representation Standard\fP\.
- XDR is the backbone of Sun's Remote Procedure Call package, in the
- sense that data for remote procedure calls is transmitted using the
- standard. XDR library routines should be used to transmit data
- that is accessed (read or written) by more than one type of machine.\**
- X.FS
- X.IX XDR "system routines"
- For a compete specification of the system eXternal Data Representation
- routines, see the
- X.I xdr (3N)
- manual page.
- X.FE
- X.LP
- This chapter contains a short tutorial overview of the XDR library
- routines, a guide to accessing currently available XDR streams, and
- information on defining new streams and data types. XDR was designed
- to work across different languages, operating systems, and machine
- architectures. Most users (particularly RPC users) will need only the
- information in sections 1, 2 and 3 of this document.
- Programmers wishing to implement RPC and XDR on new machines
- will need the information in the rest of this document, and especially the
- \fIeXternal Data Representation Standard\fP\.
- X.SH
- X.I
- NOTE:
- \fBrpcgen \fIcan be used to write XDR routines even in cases where no RPC
- calls are being made.
- X.LP
- On Sun systems,
- C programs that want to use XDR routines
- must include the file
- X.I <rpc/rpc.h> ,
- which contains all the necessary interfaces to the XDR system.
- Since the C library
- X.I libc.a
- contains all the XDR routines, compile as normal.
- X.DS
- % \fBcc\0\fIprogram\fP.c\fI
- X.DE
- X.
- X.ne 3i
- X.NH 0
- \&Justification
- X.IX "justification for XDR"
- X.IX "reason for XDR"
- X.LP
- Consider the following two programs,
- X.I writer
- X.ie t .DS
- X.el .DS L
- X.ft CW
- #include <stdio.h>
- X.sp.5
- main() /* \fIwriter.c\fP */
- {
- long i;
- X.sp.5
- for (i = 0; i < 8; i++) {
- if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- }
- }
- X.DE
- and
- X.I reader
- X.ie t .DS
- X.el .DS L
- X.ft CW
- #include <stdio.h>
- X.sp.5
- main() /* \fIreader.c\fP */
- {
- long i, j;
- X.sp.5
- for (j = 0; j < 8; j++) {
- if (fread((char *)&i, sizeof (i), 1, stdin) != 1) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- printf("%ld ", i);
- }
- printf("\en");
- }
- X.DE
- The two programs appear to be portable, because (a) they pass
- X.I lint
- checking, and (b) they exhibit the same behavior when executed
- on two different hardware architectures, a Sun and a VAX.
- X.LP
- Piping the output of the
- X.I writer
- program to the
- X.I reader
- program gives identical results on a Sun or a VAX.
- X.DS
- X.ft CW
- sun% writer | reader
- 0 1 2 3 4 5 6 7
- sun%
-
- vax% writer | reader
- 0 1 2 3 4 5 6 7
- vax%
- X.DE
- With the advent of local area networks and 4.2BSD came the concept
- of ``network pipes'' \(em a process produces data on one machine,
- and a second process consumes data on another machine.
- A network pipe can be constructed with
- X.I writer
- and
- X.I reader .
- Here are the results if the first produces data on a Sun,
- and the second consumes data on a VAX.
- X.DS
- X.ft CW
- sun% writer | rsh vax reader
- 0 16777216 33554432 50331648 67108864 83886080 100663296
- 117440512
- sun%
- X.DE
- Identical results can be obtained by executing
- X.I writer
- on the VAX and
- X.I reader
- on the Sun.
- These results occur because the byte ordering
- of long integers differs between the VAX and the Sun,
- even though word size is the same.
- Note that $16777216$ is $2 sup 24$ \(em
- when four bytes are reversed, the 1 winds up in the 24th bit.
- X.LP
- Whenever data is shared by two or more machine types,
- there is a need for portable data.
- Programs can be made data-portable by replacing the
- X.I read
- and
- X.I write
- calls with calls to an XDR library routine
- X.I xdr_long
- a filter that knows the standard representation
- of a long integer in its external form.
- Here are the revised versions of
- X.I writer :
- X.ie t .DS
- X.el .DS L
- X.ft CW
- #include <stdio.h>
- #include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */
- X.sp.5
- main() /* \fIwriter.c\fP */
- {
- XDR xdrs;
- long i;
- X.sp.5
- xdrstdio_create(&xdrs, stdout, XDR_ENCODE);
- for (i = 0; i < 8; i++) {
- if (!xdr_long(&xdrs, &i)) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- }
- }
- X.DE
- and
- X.I reader :
- X.ie t .DS
- X.el .DS L
- X.ft CW
- #include <stdio.h>
- #include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */
- X.sp.5
- main() /* \fIreader.c\fP */
- {
- XDR xdrs;
- long i, j;
- X.sp.5
- xdrstdio_create(&xdrs, stdin, XDR_DECODE);
- for (j = 0; j < 8; j++) {
- if (!xdr_long(&xdrs, &i)) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- printf("%ld ", i);
- }
- printf("\en");
- }
- X.DE
- The new programs were executed on a Sun,
- on a VAX, and from a Sun to a VAX;
- the results are shown below.
- X.DS
- X.ft CW
- sun% writer | reader
- 0 1 2 3 4 5 6 7
- sun%
-
- vax% writer | reader
- 0 1 2 3 4 5 6 7
- vax%
-
- sun% writer | rsh vax reader
- 0 1 2 3 4 5 6 7
- sun%
- X.DE
- X.SH
- X.IX "portable data"
- X.I
- NOTE:
- Integers are just the tip of the portable-data iceberg. Arbitrary
- data structures present portability problems, particularly with
- respect to alignment and pointers. Alignment on word boundaries
- may cause the size of a structure to vary from machine to machine.
- And pointers, which are very convenient to use, have no meaning
- outside the machine where they are defined.\fP
- X.LP
- X.NH 1
- \&A Canonical Standard
- X.IX XDR "canonical standard"
- X.LP
- XDR's approach to standardizing data representations is
- X.I canonical .
- That is, XDR defines a single byte order (Big Endian), a single
- floating-point representation (IEEE), and so on. Any program running on
- any machine can use XDR to create portable data by translating its
- local representation to the XDR standard representations; similarly, any
- program running on any machine can read portable data by translating the
- XDR standard representaions to its local equivalents. The single standard
- completely decouples programs that create or send portable data from those
- that use or receive portable data. The advent of a new machine or a new
- language has no effect opn the community of existing portable data creators
- and users. A new machine joins this community be being ``taught'' how to
- convert the standard representations and its local representations; the
- local representations of other machines are irrelevant. Conversely, to
- existing programs running on other machines, the local representations of
- the new machine are also irrelevant; such programs can immediately read
- portable data produced by the new machine because such data conforms to the
- canonical standards that they already understand.
- X.LP
- There are strong precedents for XDR's canonical approach. For example,
- TCP/IP, UDP/IP, XNS, Ethernet, and, indeed, all protocols below layer five
- of the ISO model, are canonical protocols. The advantage of any canonical
- approach is simplicity; in the case of XDR, a single set of conversion
- routines is written once and is never touched again. The canonical approach
- has a disadvantage, but it is unimportant in real-world data transfer
- applications. Suppose two Little-Endian machines are transferring integers
- according to the XDR standard. The sending machine converts the integers
- from Little-Endian byte order to XDR (Big-Endian) byte order; the receiving
- machine performs the reverse conversion. Because both machines observe the
- same byte order, their conversions are unnecessary. The point, however, is
- not necessity, but cost as compared to the alternative.
- X.LP
- The time spent converting to and from a canonical representation is
- insignificant, especially in networking applications. Most of the time
- required to prepare a data structure for transfer is not spent in conversion
- but in traversing the elements of the data structure. To transmit a tree,
- for example, each leaf must be visited and each element in a leaf record must
- be copied to a buffer and aligned there; storage for the leaf may have to be
- deallocated as well. Similarly, to receive a tree, storage must be
- allocated for each leaf, data must be moved from the buffer to the leaf and
- properly aligned, and pointers must be constructed to link the leaves
- together. Every machine pays the cost of traversing and copying data
- structures whether or not conversion is required. In networking
- applications, communications overhead\(emthe time required to move the data
- down through the sender's protocol layers, across the network and up through
- the receiver's protocol layers\(emdwarfs conversion overhead.
- X.NH 1
- \&The XDR Library
- X.IX "library of XDR routines"
- X.IX "XDR" "library"
- X.LP
- The XDR library not only solves data portability problems, it also
- allows you to write and read arbitrary C constructs in a consistent,
- specified, well-documented manner. Thus, it can make sense to use the
- library even when the data is not shared among machines on a network.
- X.LP
- The XDR library has filter routines for
- strings (null-terminated arrays of bytes),
- structures, unions, and arrays, to name a few.
- Using more primitive routines,
- you can write your own specific XDR routines
- to describe arbitrary data structures,
- including elements of arrays, arms of unions,
- or objects pointed at from other structures.
- The structures themselves may contain arrays of arbitrary elements,
- or pointers to other structures.
- X.LP
- Let's examine the two programs more closely.
- There is a family of XDR stream creation routines
- in which each member treats the stream of bits differently.
- In our example, data is manipulated using standard I/O routines,
- so we use
- X.I xdrstdio_create.
- The parameters to XDR stream creation routines
- vary according to their function.
- In our example,
- X.I xdrstdio_create
- takes a pointer to an XDR structure that it initializes,
- a pointer to a
- X.I FILE
- that the input or output is performed on, and the operation.
- The operation may be
- X.I XDR_ENCODE
- for serializing in the
- X.I writer
- program, or
- X.I XDR_DECODE
- for deserializing in the
- X.I reader
- program.
- X.LP
- Note: RPC users never need to create XDR streams;
- the RPC system itself creates these streams,
- which are then passed to the users.
- X.LP
- The
- X.I xdr_long
- primitive is characteristic of most XDR library
- primitives and all client XDR routines.
- First, the routine returns
- X.I FALSE
- (0) if it fails, and
- X.I TRUE
- (1) if it succeeds.
- Second, for each data type,
- X.I xxx ,
- there is an associated XDR routine of the form:
- X.DS
- X.ft CW
- xdr_xxx(xdrs, xp)
- XDR *xdrs;
- xxx *xp;
- {
- }
- X.DE
- In our case,
- X.I xxx
- is long, and the corresponding XDR routine is
- a primitive,
- X.I xdr_long
- The client could also define an arbitrary structure
- X.I xxx
- in which case the client would also supply the routine
- X.I xdr_xxx ,
- describing each field by calling XDR routines
- of the appropriate type.
- In all cases the first parameter,
- X.I xdrs
- can be treated as an opaque handle,
- and passed to the primitive routines.
- X.LP
- XDR routines are direction independent;
- that is, the same routines are called to serialize or deserialize data.
- This feature is critical to software engineering of portable data.
- The idea is to call the same routine for either operation \(em
- this almost guarantees that serialized data can also be deserialized.
- One routine is used by both producer and consumer of networked data.
- This is implemented by always passing the address
- of an object rather than the object itself \(em
- only in the case of deserialization is the object modified.
- This feature is not shown in our trivial example,
- but its value becomes obvious when nontrivial data structures
- are passed among machines. If needed, the user can obtain the
- direction of the XDR operation. See the
- \fIXDR Operation Directions\fP
- section of this chapter for details.
- X.LP
- Let's look at a slightly more complicated example.
- Assume that a person's gross assets and liabilities
- are to be exchanged among processes.
- Also assume that these values are important enough
- to warrant their own data type:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct gnumbers {
- long g_assets;
- long g_liabilities;
- };
- X.DE
- The corresponding XDR routine describing this structure would be:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- bool_t /* \fITRUE is success, FALSE is failure\fP */
- xdr_gnumbers(xdrs, gp)
- XDR *xdrs;
- struct gnumbers *gp;
- {
- if (xdr_long(xdrs, &gp->g_assets) &&
- xdr_long(xdrs, &gp->g_liabilities))
- return(TRUE);
- return(FALSE);
- }
- X.DE
- Note that the parameter
- X.I xdrs
- is never inspected or modified;
- it is only passed on to the subcomponent routines.
- It is imperative to inspect the return value of each XDR routine call,
- and to give up immediately and return
- X.I FALSE
- if the subroutine fails.
- X.LP
- This example also shows that the type
- X.I bool_t
- is declared as an integer whose only values are
- X.I TRUE
- (1) and
- X.I FALSE
- (0). This document uses the following definitions:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- #define bool_t int
- #define TRUE 1
- #define FALSE 0
- X.sp.5
- #define enum_t int /* \fIenum_t used for generic enums\fP */
- X.DE
- X.LP
- Keeping these conventions in mind,
- X.I xdr_gnumbers
- can be rewritten as follows:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- xdr_gnumbers(xdrs, gp)
- XDR *xdrs;
- struct gnumbers *gp;
- {
- return(xdr_long(xdrs, &gp->g_assets) &&
- xdr_long(xdrs, &gp->g_liabilities));
- }
- X.DE
- This document uses both coding styles.
- X.
- X.NH 1
- \&XDR Library Primitives
- X.IX "library primitives for XDR"
- X.IX "XDR "library primitives"
- X.LP
- This section gives a synopsis of each XDR primitive.
- It starts with basic data types and moves on to constructed data types.
- Finally, XDR utilities are discussed.
- The interface to these primitives
- and utilities is defined in the include file
- X.I <rpc/xdr.h> ,
- automatically included by
- X.I <rpc/rpc.h> .
- X.
- X.NH 2
- \&Number Filters
- X.IX "XDR library" "number filters"
- X.LP
- The XDR library provides primitives to translate between numbers
- and their corresponding external representations.
- Primitives cover the set of numbers in:
- X.EQ
- [signed, unsigned] * [short, int, long]
- X.EN
- Specifically, the eight primitives are:
- X.DS
- X.ft CW
- bool_t xdr_char(xdrs, cp)
- XDR *xdrs;
- char *cp;
- X.sp.5
- bool_t xdr_u_char(xdrs, ucp)
- XDR *xdrs;
- unsigned char *ucp;
- X.sp.5
- bool_t xdr_int(xdrs, ip)
- XDR *xdrs;
- int *ip;
- X.sp.5
- bool_t xdr_u_int(xdrs, up)
- XDR *xdrs;
- unsigned *up;
- X.sp.5
- bool_t xdr_long(xdrs, lip)
- XDR *xdrs;
- long *lip;
- X.sp.5
- bool_t xdr_u_long(xdrs, lup)
- XDR *xdrs;
- u_long *lup;
- X.sp.5
- bool_t xdr_short(xdrs, sip)
- XDR *xdrs;
- short *sip;
- X.sp.5
- bool_t xdr_u_short(xdrs, sup)
- XDR *xdrs;
- u_short *sup;
- X.DE
- The first parameter,
- X.I xdrs ,
- is an XDR stream handle.
- The second parameter is the address of the number
- that provides data to the stream or receives data from it.
- All routines return
- X.I TRUE
- if they complete successfully, and
- X.I FALSE
- otherwise.
- X.
- X.NH 2
- \&Floating Point Filters
- X.IX "XDR library" "floating point filters"
- X.LP
- The XDR library also provides primitive routines
- for C's floating point types:
- X.DS
- X.ft CW
- bool_t xdr_float(xdrs, fp)
- XDR *xdrs;
- float *fp;
- X.sp.5
- bool_t xdr_double(xdrs, dp)
- XDR *xdrs;
- double *dp;
- X.DE
- The first parameter,
- X.I xdrs
- is an XDR stream handle.
- The second parameter is the address
- of the floating point number that provides data to the stream
- or receives data from it.
- All routines return
- X.I TRUE
- if they complete successfully, and
- X.I FALSE
- otherwise.
- X.LP
- Note: Since the numbers are represented in IEEE floating point,
- routines may fail when decoding a valid IEEE representation
- into a machine-specific representation, or vice-versa.
- X.
- X.NH 2
- \&Enumeration Filters
- X.IX "XDR library" "enumeration filters"
- X.LP
- The XDR library provides a primitive for generic enumerations.
- The primitive assumes that a C
- X.I enum
- has the same representation inside the machine as a C integer.
- The boolean type is an important instance of the
- X.I enum .
- The external representation of a boolean is always
- X.I TRUE
- (1) or
- X.I FALSE
- (0).
- X.DS
- X.ft CW
- #define bool_t int
- #define FALSE 0
- #define TRUE 1
- X.sp.5
- #define enum_t int
- X.sp.5
- bool_t xdr_enum(xdrs, ep)
- XDR *xdrs;
- enum_t *ep;
- X.sp.5
- bool_t xdr_bool(xdrs, bp)
- XDR *xdrs;
- bool_t *bp;
- X.DE
- The second parameters
- X.I ep
- and
- X.I bp
- are addresses of the associated type
- that provides data to, or receives data from, the stream
- X.I xdrs
- The routine returns
- X.I FALSE
- if the number of characters exceeds
- X.I maxlength ,
- and
- X.I TRUE
- if it doesn't.
- X.
- X.NH 2
- \&No Data
- X.IX "XDR library" "no data"
- X.LP
- Occasionally, an XDR routine must be supplied to the RPC system,
- even when no data is passed or required.
- The library provides such a routine:
- X.DS
- X.ft CW
- bool_t xdr_void(); /* \fIalways returns TRUE\fP */
- X.DE
- X.
- X.NH 2
- \&Constructed Data Type Filters
- X.IX "XDR library" "constructed data type filters"
- X.LP
- Constructed or compound data type primitives
- require more parameters and perform more complicated functions
- then the primitives discussed above.
- This section includes primitives for
- strings, arrays, unions, and pointers to structures.
- X.LP
- Constructed data type primitives may use memory management.
- In many cases, memory is allocated when deserializing data with
- X.I XDR_DECODE
- Therefore, the XDR package must provide means to deallocate memory.
- This is done by an XDR operation,
- X.I XDR_FREE
- To review, the three XDR directional operations are
- X.I XDR_ENCODE ,
- X.I XDR_DECODE
- and
- X.I XDR_FREE .
- X.
- X.NH 3
- \&Strings
- X.IX "XDR library" "strings"
- X.IX "strings"
- X.LP
- In C, a string is defined as a sequence of bytes
- terminated by a null byte,
- which is not considered when calculating string length.
- However, when a string is passed or manipulated,
- a pointer to it is employed.
- Therefore, the XDR library defines a string to be a
- X.I "char
- and not a sequence of characters.
- The external representation of a string is drastically different
- from its internal representation.
- Externally, strings are represented as
- sequences of ASCII characters,
- while internally, they are represented with character pointers.
- Conversion between the two representations
- is accomplished with the routine
- X.I xdr_string.
- X.DS
- X.ft CW
- bool_t xdr_string(xdrs, sp, maxlength)
- XDR *xdrs;
- char **sp;
- u_int maxlength;
- X.DE
- The first parameter
- X.I xdrs
- is the XDR stream handle.
- The second parameter
- X.I sp
- is a pointer to a string (type
- X.I "char
- The third parameter
- X.I maxlength
- specifies the maximum number of bytes allowed during encoding or decoding;
- its value is usually specified by a protocol.
- For example, a protocol specification may say
- that a file name may be no longer than 255 characters.
- The routine returns
- X.I FALSE
- if the number of characters exceeds
- X.I maxlength ,
- and
- X.I TRUE
- if it doesn't.
- X.LP
- The behavior of
- X.I xdr_string
- is similar to the behavior of other routines
- discussed in this section. The direction
- X.I XDR_ENCODE
- is easiest to understand. The parameter
- X.I sp
- points to a string of a certain length;
- if the string does not exceed
- X.I maxlength ,
- the bytes are serialized.
- X.LP
- The effect of deserializing a string is subtle.
- First the length of the incoming string is determined;
- it must not exceed
- X.I maxlength .
- Next
- X.I sp
- is dereferenced; if the the value is
- X.I NULL ,
- then a string of the appropriate length is allocated and
- X.I *sp
- is set to this string.
- If the original value of
- X.I *sp
- is non-null, then the XDR package assumes
- that a target area has been allocated,
- which can hold strings no longer than
- X.I maxlength .
- In either case, the string is decoded into the target area.
- The routine then appends a null character to the string.
- X.LP
- In the
- X.I XDR_FREE
- operation, the string is obtained by dereferencing
- X.I sp .
- If the string is not
- X.I NULL ,
- it is freed and
- X.I *sp
- is set to
- X.I NULL .
- In this operation,
- X.I xdr_string
- ignores the
- X.I maxlength
- parameter.
- X.
- X.NH 3
- \&Byte Arrays
- X.IX "XDR library" "byte arrays"
- X.IX "byte arrays"
- X.LP
- Often variable-length arrays of bytes are preferable to strings.
- Byte arrays differ from strings in the following three ways:
- 1) the length of the array (the byte count) is explicitly
- located in an unsigned integer,
- 2) the byte sequence is not terminated by a null character, and
- 3) the external representation of the bytes is the same as their
- internal representation.
- The primitive
- X.I xdr_bytes
- converts between the internal and external
- representations of byte arrays:
- X.DS
- X.ft CW
- bool_t xdr_bytes(xdrs, bpp, lp, maxlength)
- XDR *xdrs;
- char **bpp;
- u_int *lp;
- u_int maxlength;
- X.DE
- The usage of the first, second and fourth parameters
- are identical to the first, second and third parameters of
- X.I xdr_string ,
- respectively.
- The length of the byte area is obtained by dereferencing
- X.I lp
- when serializing;
- X.I *lp
- is set to the byte length when deserializing.
- X.
- X.NH 3
- \&Arrays
- X.IX "XDR library" "arrays"
- X.IX "arrays"
- X.LP
- The XDR library package provides a primitive
- for handling arrays of arbitrary elements.
- The
- X.I xdr_bytes
- routine treats a subset of generic arrays,
- in which the size of array elements is known to be 1,
- and the external description of each element is built-in.
- The generic array primitive,
- X.I xdr_array
- requires parameters identical to those of
- X.I xdr_bytes
- plus two more:
- the size of array elements,
- and an XDR routine to handle each of the elements.
- This routine is called to encode or decode
- each element of the array.
- X.DS
- X.ft CW
- bool_t
- xdr_array(xdrs, ap, lp, maxlength, elementsiz, xdr_element)
- XDR *xdrs;
- char **ap;
- u_int *lp;
- u_int maxlength;
- u_int elementsiz;
- bool_t (*xdr_element)();
- X.DE
- The parameter
- X.I ap
- is the address of the pointer to the array.
- If
- X.I *ap
- is
- X.I NULL
- when the array is being deserialized,
- XDR allocates an array of the appropriate size and sets
- X.I *ap
- to that array.
- The element count of the array is obtained from
- X.I *lp
- when the array is serialized;
- X.I *lp
- is set to the array length when the array is deserialized.
- The parameter
- X.I maxlength
- is the maximum number of elements that the array is allowed to have;
- X.I elementsiz
- is the byte size of each element of the array
- (the C function
- X.I sizeof
- can be used to obtain this value).
- The routine
- X.I xdr_element
- is called to serialize, deserialize, or free
- each element of the array.
- X.
- X.LP
- Before defining more constructed data types, it is appropriate to
- present three examples.
- X.LP
- X.I "Example A:"
- X.br
- A user on a networked machine can be identified by
- (a) the machine name, such as
- X.I krypton :
- see the
- X.I gethostname
- man page; (b) the user's UID: see the
- X.I geteuid
- man page; and (c) the group numbers to which the user belongs:
- see the
- X.I getgroups
- man page. A structure with this information and its associated
- XDR routine could be coded like this:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct netuser {
- char *nu_machinename;
- int nu_uid;
- u_int nu_glen;
- int *nu_gids;
- };
- #define NLEN 255 /* \fImachine names < 256 chars\fP */
- #define NGRPS 20 /* \fIuser can't be in > 20 groups\fP */
- X.sp.5
- bool_t
- xdr_netuser(xdrs, nup)
- XDR *xdrs;
- struct netuser *nup;
- {
- return(xdr_string(xdrs, &nup->nu_machinename, NLEN) &&
- xdr_int(xdrs, &nup->nu_uid) &&
- xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen,
- NGRPS, sizeof (int), xdr_int));
- }
- X.DE
- X.LP
- X.I "Example B:"
- X.br
- A party of network users could be implemented
- as an array of
- X.I netuser
- structure.
- The declaration and its associated XDR routines
- are as follows:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct party {
- u_int p_len;
- struct netuser *p_nusers;
- };
- #define PLEN 500 /* \fImax number of users in a party\fP */
- X.sp.5
- bool_t
- xdr_party(xdrs, pp)
- XDR *xdrs;
- struct party *pp;
- {
- return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN,
- sizeof (struct netuser), xdr_netuser));
- }
- X.DE
- X.LP
- X.I "Example C:"
- X.br
- The well-known parameters to
- X.I main ,
- X.I argc
- and
- X.I argv
- can be combined into a structure.
- An array of these structures can make up a history of commands.
- The declarations and XDR routines might look like:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct cmd {
- u_int c_argc;
- char **c_argv;
- };
- #define ALEN 1000 /* \fIargs cannot be > 1000 chars\fP */
- #define NARGC 100 /* \fIcommands cannot have > 100 args\fP */
-
- struct history {
- u_int h_len;
- struct cmd *h_cmds;
- };
- #define NCMDS 75 /* \fIhistory is no more than 75 commands\fP */
-
- bool_t
- xdr_wrap_string(xdrs, sp)
- XDR *xdrs;
- char **sp;
- {
- return(xdr_string(xdrs, sp, ALEN));
- }
- X.DE
- X.ie t .DS
- X.el .DS L
- X.ft CW
- bool_t
- xdr_cmd(xdrs, cp)
- XDR *xdrs;
- struct cmd *cp;
- {
- return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC,
- sizeof (char *), xdr_wrap_string));
- }
- X.DE
- X.ie t .DS
- X.el .DS L
- X.ft CW
- bool_t
- xdr_history(xdrs, hp)
- XDR *xdrs;
- struct history *hp;
- {
- return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS,
- sizeof (struct cmd), xdr_cmd));
- }
- X.DE
- The most confusing part of this example is that the routine
- X.I xdr_wrap_string
- is needed to package the
- X.I xdr_string
- routine, because the implementation of
- X.I xdr_array
- only passes two parameters to the array element description routine;
- X.I xdr_wrap_string
- supplies the third parameter to
- X.I xdr_string .
- X.LP
- By now the recursive nature of the XDR library should be obvious.
- Let's continue with more constructed data types.
- X.
- X.NH 3
- \&Opaque Data
- X.IX "XDR library" "opaque data"
- X.IX "opaque data"
- X.LP
- In some protocols, handles are passed from a server to client.
- The client passes the handle back to the server at some later time.
- Handles are never inspected by clients;
- they are obtained and submitted.
- That is to say, handles are opaque.
- The primitive
- X.I xdr_opaque
- is used for describing fixed sized, opaque bytes.
- X.DS
- X.ft CW
- bool_t xdr_opaque(xdrs, p, len)
- XDR *xdrs;
- char *p;
- u_int len;
- X.DE
- The parameter
- X.I p
- is the location of the bytes;
- X.I len
- is the number of bytes in the opaque object.
- By definition, the actual data
- contained in the opaque object are not machine portable.
- X.
- X.NH 3
- \&Fixed Sized Arrays
- X.IX "XDR library" "fixed sized arrays"
- X.IX "fixed sized arrays"
- X.LP
- The XDR library provides a primitive,
- X.I xdr_vector ,
- for fixed-length arrays.
- X.ie t .DS
- X.el .DS L
- X.ft CW
- #define NLEN 255 /* \fImachine names must be < 256 chars\fP */
- #define NGRPS 20 /* \fIuser belongs to exactly 20 groups\fP */
- X.sp.5
- struct netuser {
- char *nu_machinename;
- int nu_uid;
- int nu_gids[NGRPS];
- };
- X.sp.5
- bool_t
- xdr_netuser(xdrs, nup)
- XDR *xdrs;
- struct netuser *nup;
- {
- int i;
- X.sp.5
- if (!xdr_string(xdrs, &nup->nu_machinename, NLEN))
- return(FALSE);
- if (!xdr_int(xdrs, &nup->nu_uid))
- return(FALSE);
- if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int),
- xdr_int)) {
- return(FALSE);
- }
- return(TRUE);
- }
- X.DE
- X.
- X.NH 3
- \&Discriminated Unions
- X.IX "XDR library" "discriminated unions"
- X.IX "discriminated unions"
- X.LP
- The XDR library supports discriminated unions.
- A discriminated union is a C union and an
- X.I enum_t
- value that selects an ``arm'' of the union.
- X.DS
- X.ft CW
- struct xdr_discrim {
- enum_t value;
- bool_t (*proc)();
- };
- X.sp.5
- bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm)
- XDR *xdrs;
- enum_t *dscmp;
- char *unp;
- struct xdr_discrim *arms;
- bool_t (*defaultarm)(); /* \fImay equal NULL\fP */
- X.DE
- First the routine translates the discriminant of the union located at
- X.I *dscmp .
- The discriminant is always an
- X.I enum_t
- Next the union located at
- X.I *unp
- is translated.
- The parameter
- X.I arms
- is a pointer to an array of
- X.I xdr_discrim
- structures.
- Each structure contains an order pair of
- X.I [value,proc] .
- If the union's discriminant is equal to the associated
- X.I value
- then the
- X.I proc
- is called to translate the union.
- The end of the
- X.I xdr_discrim
- structure array is denoted by a routine of value
- X.I NULL
- (0). If the discriminant is not found in the
- X.I arms
- array, then the
- X.I defaultarm
- procedure is called if it is non-null;
- otherwise the routine returns
- X.I FALSE .
- X.LP
- X.I "Example D:"
- Suppose the type of a union may be integer,
- character pointer (a string), or a
- X.I gnumbers
- structure.
- Also, assume the union and its current type
- are declared in a structure.
- The declaration is:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- enum utype { INTEGER=1, STRING=2, GNUMBERS=3 };
- X.sp.5
- struct u_tag {
- enum utype utype; /* \fIthe union's discriminant\fP */
- union {
- int ival;
- char *pval;
- struct gnumbers gn;
- } uval;
- };
- X.DE
- The following constructs and XDR procedure (de)serialize
- the discriminated union:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct xdr_discrim u_tag_arms[4] = {
- { INTEGER, xdr_int },
- { GNUMBERS, xdr_gnumbers }
- { STRING, xdr_wrap_string },
- { __dontcare__, NULL }
- /* \fIalways terminate arms with a NULL xdr_proc\fP */
- }
- X.sp.5
- bool_t
- xdr_u_tag(xdrs, utp)
- XDR *xdrs;
- struct u_tag *utp;
- {
- return(xdr_union(xdrs, &utp->utype, &utp->uval,
- u_tag_arms, NULL));
- }
- X.DE
- The routine
- X.I xdr_gnumbers
- was presented above in the
- \fIThe XDR Library\fP
- section.
- X.I xdr_wrap_string
- was presented in example C.
- The default
- X.I arm
- parameter to
- X.I xdr_union
- (the last parameter) is
- X.I NULL
- in this example. Therefore the value of the union's discriminant
- may legally take on only values listed in the
- X.I u_tag_arms
- array. This example also demonstrates that
- the elements of the arm's array do not need to be sorted.
- X.LP
- It is worth pointing out that the values of the discriminant
- may be sparse, though in this example they are not.
- It is always good
- practice to assign explicitly integer values to each element of the
- discriminant's type.
- This practice both documents the external
- representation of the discriminant and guarantees that different
- C compilers emit identical discriminant values.
- X.LP
- Exercise: Implement
- X.I xdr_union
- using the other primitives in this section.
- X.
- X.NH 3
- \&Pointers
- X.IX "XDR library" "pointers"
- X.IX "pointers"
- X.LP
- In C it is often convenient to put pointers
- to another structure within a structure.
- The primitive
- X.I xdr_reference
- makes it easy to serialize, deserialize, and free
- these referenced structures.
- X.DS
- X.ft CW
- bool_t xdr_reference(xdrs, pp, size, proc)
- XDR *xdrs;
- char **pp;
- u_int ssize;
- bool_t (*proc)();
- X.DE
- X.LP
- Parameter
- X.I pp
- is the address of
- the pointer to the structure;
- parameter
- X.I ssize
- is the size in bytes of the structure
- (use the C function
- X.I sizeof
- to obtain this value); and
- X.I proc
- is the XDR routine that describes the structure.
- When decoding data, storage is allocated if
- X.I *pp
- is
- X.I NULL .
- X.LP
- There is no need for a primitive
- X.I xdr_struct
- to describe structures within structures,
- because pointers are always sufficient.
- X.LP
- Exercise: Implement
- X.I xdr_reference
- using
- X.I xdr_array .
- Warning:
- X.I xdr_reference
- and
- X.I xdr_array
- are NOT interchangeable external representations of data.
- X.LP
- X.I "Example E:"
- Suppose there is a structure containing a person's name
- and a pointer to a
- X.I gnumbers
- structure containing the person's gross assets and liabilities.
- The construct is:
- X.DS
- X.ft CW
- struct pgn {
- char *name;
- struct gnumbers *gnp;
- };
- X.DE
- The corresponding XDR routine for this structure is:
- X.DS
- X.ft CW
- bool_t
- xdr_pgn(xdrs, pp)
- XDR *xdrs;
- struct pgn *pp;
- {
- if (xdr_string(xdrs, &pp->name, NLEN) &&
- xdr_reference(xdrs, &pp->gnp,
- sizeof(struct gnumbers), xdr_gnumbers))
- return(TRUE);
- return(FALSE);
- }
- X.DE
- X.
- X.IX "pointer semantics and XDR"
- X.I "Pointer Semantics and XDR"
- X.LP
- In many applications, C programmers attach double meaning to
- the values of a pointer. Typically the value
- X.I NULL
- (or zero) means data is not needed,
- yet some application-specific interpretation applies.
- In essence, the C programmer is encoding
- a discriminated union efficiently
- by overloading the interpretation of the value of a pointer.
- For instance, in example E a
- X.I NULL
- pointer value for
- X.I gnp
- could indicate that
- the person's assets and liabilities are unknown.
- That is, the pointer value encodes two things:
- whether or not the data is known;
- and if it is known, where it is located in memory.
- Linked lists are an extreme example of the use
- of application-specific pointer interpretation.
- X.LP
- The primitive
- X.I xdr_reference
- cannot and does not attach any special
- meaning to a null-value pointer during serialization.
- That is, passing an address of a pointer whose value is
- X.I NULL
- to
- X.I xdr_reference
- when serialing data will most likely cause a memory fault and, on the UNIX
- system, a core dump.
- X.LP
- X.I xdr_pointer
- correctly handles
- X.I NULL
- pointers. For more information about its use, see
- \fILinked Lists\fP.
- X.LP
- X.I Exercise:
- After reading the section on
- \fILinked Lists\fP\,
- return here and extend example E so that
- it can correctly deal with
- X.I NULL
- pointer values.
- X.LP
- X.I Exercise:
- Using the
- X.I xdr_union
- X.I xdr_reference
- and
- X.I xdr_void
- primitives, implement a generic pointer handling primitive
- that implicitly deals with
- X.I NULL
- pointers. That is, implement
- X.I xdr_pointer .
- X.
- X.NH 2
- \&Non-filter Primitives
- X.IX "XDR" "non-filter primitives"
- X.IX "non-filter primitives"
- X.LP
- XDR streams can be manipulated with
- the primitives discussed in this section.
- X.DS
- X.ft CW
- u_int xdr_getpos(xdrs)
- XDR *xdrs;
- X.sp.5
- bool_t xdr_setpos(xdrs, pos)
- XDR *xdrs;
- u_int pos;
- X.sp.5
- xdr_destroy(xdrs)
- XDR *xdrs;
- X.DE
- The routine
- X.I xdr_getpos
- returns an unsigned integer
- that describes the current position in the data stream.
- Warning: In some XDR streams, the returned value of
- X.I xdr_getpos
- is meaningless;
- the routine returns a \-1 in this case
- (though \-1 should be a legitimate value).
- X.LP
- The routine
- X.I xdr_setpos
- sets a stream position to
- X.I pos
- Warning: In some XDR streams, setting a position is impossible;
- in such cases,
- X.I xdr_setpos
- will return
- X.I FALSE .
- This routine will also fail if the requested position is out-of-bounds.
- The definition of bounds varies from stream to stream.
- X.LP
- The
- X.I xdr_destroy
- primitive destroys the XDR stream.
- Usage of the stream
- after calling this routine is undefined.
- X.
- X.NH 2
- \&XDR Operation Directions
- X.IX "XDR operation directions"
- X.IX "direction of XDR operations"
- X.LP
- At times you may wish to optimize XDR routines by taking
- advantage of the direction of the operation \(em
- X.I XDR_ENCODE
- X.I XDR_DECODE
- or
- X.I XDR_FREE
- The value
- X.I xdrs->x_op
- always contains the
- direction of the XDR operation.
- Programmers are not encouraged to take advantage of this information.
- Therefore, no example is presented here.
- However, an example in Section 7
- demonstrates the usefulness of the
- X.I xdrs->x_op
- field.
- X.
- X.NH 2
- \&XDR Stream Access
- X.IX "XDR" "stream access"
- X.IX "stream access"
- X.LP
- An XDR stream is obtained by calling the appropriate creation routine.
- These creation routines take arguments that are tailored to the
- specific properties of the stream.
- X.LP
- Streams currently exist for (de)serialization of data to or from
- standard I/O
- X.I FILE
- streams, TCP/IP connections and UNIX files, and memory.
- Section 5 documents the XDR object and how to make
- new XDR streams when they are required.
- X.
- X.NH 3
- \&Standard I/O Streams
- X.IX "XDR" "standard I/O streams"
- X.IX "standard I/O streams"
- X.LP
- XDR streams can be interfaced to standard I/O using the
- X.I xdrstdio_create
- routine as follows:
- X.DS
- X.ft CW
- #include <stdio.h>
- #include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */
- X.sp.5
- void
- xdrstdio_create(xdrs, fp, x_op)
- XDR *xdrs;
- FILE *fp;
- enum xdr_op x_op;
- X.DE
- The routine
- X.I xdrstdio_create
- initializes an XDR stream pointed to by
- X.I xdrs
- The XDR stream interfaces to the standard I/O library.
- Parameter
- X.I fp
- is an open file, and
- X.I x_op
- is an XDR direction.
- X.
- X.NH 3
- \&Memory Streams
- X.IX "XDR" "memory streams"
- X.IX "memory streams"
- X.LP
- Memory streams allow the streaming of data into or out of
- a specified area of memory:
- X.DS
- X.ft CW
- #include <rpc/rpc.h>
- X.sp.5
- void
- xdrmem_create(xdrs, addr, len, x_op)
- XDR *xdrs;
- char *addr;
- u_int len;
- enum xdr_op x_op;
- X.DE
- The routine
- X.I xdrmem_create
- initializes an XDR stream in local memory.
- The memory is pointed to by parameter
- X.I addr
- parameter
- X.I len
- is the length in bytes of the memory.
- The parameters
- X.I xdrs
- and
- X.I x_op
- are identical to the corresponding parameters of
- X.I xdrstdio_create
- Currently, the UDP/IP implementation of RPC uses
- X.I xdrmem_create
- Complete call or result messages are built in memory before calling the
- X.I sendto
- system routine.
- X.
- X.NH 3
- \&Record (TCP/IP) Streams
- X.IX "XDR" "record (TCP/IP) streams"
- X.IX "record (TCP/IP) streams"
- X.LP
- A record stream is an XDR stream built on top of
- a record marking standard that is built on top of the
- UNIX file or 4.2 BSD connection interface.
- X.DS
- X.ft CW
- #include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */
- X.sp.5
- xdrrec_create(xdrs,
- sendsize, recvsize, iohandle, readproc, writeproc)
- XDR *xdrs;
- u_int sendsize, recvsize;
- char *iohandle;
- int (*readproc)(), (*writeproc)();
- X.DE
- The routine
- X.I xdrrec_create
- provides an XDR stream interface that allows for a bidirectional,
- arbitrarily long sequence of records.
- The contents of the records are meant to be data in XDR form.
- The stream's primary use is for interfacing RPC to TCP connections.
- However, it can be used to stream data into or out of normal
- UNIX files.
- X.LP
- The parameter
- X.I xdrs
- is similar to the corresponding parameter described above.
- The stream does its own data buffering similar to that of standard I/O.
- The parameters
- X.I sendsize
- and
- X.I recvsize
- determine the size in bytes of the output and input buffers, respectively;
- if their values are zero (0), then predetermined defaults are used.
- When a buffer needs to be filled or flushed, the routine
- X.I readproc
- or
- X.I writeproc
- is called, respectively.
- The usage and behavior of these
- routines are similar to the UNIX system calls
- X.I read
- and
- X.I write .
- However,
- the first parameter to each of these routines is the opaque parameter
- X.I iohandle
- The other two parameters
- X.I buf
- and
- X.I nbytes
- and the results
- (byte count) are identical to the system routines.
- If
- X.I xxx
- is
- X.I readproc
- or
- X.I writeproc
- then it has the following form:
- X.DS
- X.ft CW
- X.ft I
- /*
- * returns the actual number of bytes transferred.
- * -1 is an error
- */
- X.ft CW
- int
- xxx(iohandle, buf, len)
- char *iohandle;
- char *buf;
- int nbytes;
- X.DE
- The XDR stream provides means for delimiting records in the byte stream.
- The implementation details of delimiting records in a stream
- are discussed in appendix 1.
- The primitives that are specific to record streams are as follows:
- X.DS
- X.ft CW
- bool_t
- xdrrec_endofrecord(xdrs, flushnow)
- XDR *xdrs;
- bool_t flushnow;
- X.sp.5
- bool_t
- xdrrec_skiprecord(xdrs)
- XDR *xdrs;
- X.sp.5
- bool_t
- xdrrec_eof(xdrs)
- XDR *xdrs;
- X.DE
- The routine
- X.I xdrrec_endofrecord
- causes the current outgoing data to be marked as a record.
- If the parameter
- X.I flushnow
- is
- X.I TRUE ,
- then the stream's
- X.I writeproc
- will be called; otherwise,
- X.I writeproc
- will be called when the output buffer has been filled.
- X.LP
- The routine
- X.I xdrrec_skiprecord
- causes an input stream's position to be moved past
- the current record boundary and onto the
- beginning of the next record in the stream.
- X.LP
- If there is no more data in the stream's input buffer,
- then the routine
- X.I xdrrec_eof
- returns
- X.I TRUE .
- That is not to say that there is no more data
- in the underlying file descriptor.
- X.
- X.NH 2
- \&XDR Stream Implementation
- X.IX "XDR" "stream implementation"
- X.IX "stream implementation in XDR"
- X.LP
- This section provides the abstract data types needed
- to implement new instances of XDR streams.
- X.
- X.NH 3
- \&The XDR Object
- X.IX "XDR" "object"
- X.IX "object"
- X.LP
- The following structure defines the interface to an XDR stream:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- enum xdr_op { XDR_ENCODE=0, XDR_DECODE=1, XDR_FREE=2 };
- X.sp.5
- typedef struct {
- enum xdr_op x_op; /* \fIoperation; fast added param\fP */
- struct xdr_ops {
- bool_t (*x_getlong)(); /* \fIget long from stream\fP */
- bool_t (*x_putlong)(); /* \fIput long to stream\fP */
- bool_t (*x_getbytes)(); /* \fIget bytes from stream\fP */
- bool_t (*x_putbytes)(); /* \fIput bytes to stream\fP */
- u_int (*x_getpostn)(); /* \fIreturn stream offset\fP */
- bool_t (*x_setpostn)(); /* \fIreposition offset\fP */
- caddr_t (*x_inline)(); /* \fIptr to buffered data\fP */
- VOID (*x_destroy)(); /* \fIfree private area\fP */
- } *x_ops;
- caddr_t x_public; /* \fIusers' data\fP */
- caddr_t x_private; /* \fIpointer to private data\fP */
- caddr_t x_base; /* \fIprivate for position info\fP */
- int x_handy; /* \fIextra private word\fP */
- } XDR;
- X.DE
- The
- X.I x_op
- field is the current operation being performed on the stream.
- This field is important to the XDR primitives,
- but should not affect a stream's implementation.
- That is, a stream's implementation should not depend
- on this value.
- The fields
- X.I x_private
- X.I x_base
- and
- X.I x_handy
- are private to the particular
- stream's implementation.
- The field
- X.I x_public
- is for the XDR client and should never be used by
- the XDR stream implementations or the XDR primitives.
- X.LP
- Macros for accessing operations
- X.I x_getpostn
- X.I x_setpostn
- and
- X.I x_destroy
- were defined in Section 3.6.
- The operation
- X.I x_inline
- takes two parameters:
- an XDR *, and an unsigned integer, which is a byte count.
- The routine returns a pointer to a piece of
- the stream's internal buffer.
- The caller can then use the buffer segment for any purpose.
- From the stream's point of view, the bytes in the
- buffer segment have been consumed or put.
- The routine may return
- X.I NULL
- if it cannot return a buffer segment of the requested size.
- (The
- X.I x_inline
- routine is for cycle squeezers.
- Use of the resulting buffer is not data-portable.
- Users are encouraged not to use this feature.)
- X.LP
- The operations
- X.I x_getbytes
- and
- X.I x_putbytes
- blindly get and put sequences of bytes
- from or to the underlying stream;
- they return
- X.I TRUE
- if they are successful, and
- X.I FALSE
- otherwise. The routines have identical parameters (replace
- X.I xxx ):
- X.DS
- X.ft CW
- bool_t
- xxxbytes(xdrs, buf, bytecount)
- XDR *xdrs;
- char *buf;
- u_int bytecount;
- X.DE
- The operations
- X.I x_getlong
- and
- X.I x_putlong
- receive and put
- long numbers from and to the data stream.
- It is the responsibility of these routines
- to translate the numbers between the machine representation
- and the (standard) external representation.
- The UNIX primitives
- X.I htonl
- and
- X.I ntohl
- can be helpful in accomplishing this.
- Section 6 defines the standard representation of numbers.
- The higher-level XDR implementation assumes that
- signed and unsigned long integers contain the same number of bits,
- and that nonnegative integers
- have the same bit representations as unsigned integers.
- The routines return
- X.I TRUE
- if they succeed, and
- X.I FALSE
- otherwise. They have identical parameters:
- X.DS
- X.ft CW
- bool_t
- xxxlong(xdrs, lp)
- XDR *xdrs;
- long *lp;
- X.DE
- Implementors of new XDR streams must make an XDR structure
- (with new operation routines) available to clients,
- using some kind of create routine.
- X.
- X.NH 1
- \&Advanced Topics
- X.IX XDR "advanced topics"
- X.IX "advanced topics"
- X.LP
- This section describes techniques for passing data structures that
- are not covered in the preceding sections. Such structures include
- linked lists (of arbitrary lengths). Unlike the simpler examples
- covered in the earlier sections, the following examples are written
- using both the XDR C library routines and the XDR data description
- language. The
- \fIeXternal Data Representation Standard\fP
- chapter of this
- X.I "Networking Programming"
- manual describes this language in complete detail.
- X.
- X.NH 2
- \&Linked Lists
- X.IX "linked lists"
- X.IX XDR "linked lists"
- X.LP
- The last example in the
- \fIPointers\fP
- section presented a C data structure and its associated XDR
- routines for a individual's gross assets and liabilities.
- The example is duplicated below:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct gnumbers {
- long g_assets;
- long g_liabilities;
- };
- X.sp.5
- bool_t
- xdr_gnumbers(xdrs, gp)
- XDR *xdrs;
- struct gnumbers *gp;
- {
- if (xdr_long(xdrs, &(gp->g_assets)))
- return(xdr_long(xdrs, &(gp->g_liabilities)));
- return(FALSE);
- }
- X.DE
- X.LP
- Now assume that we wish to implement a linked list of such information.
- A data structure could be constructed as follows:
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct gnumbers_node {
- struct gnumbers gn_numbers;
- struct gnumbers_node *gn_next;
- };
- X.sp .5
- typedef struct gnumbers_node *gnumbers_list;
- X.DE
- X.LP
- The head of the linked list can be thought of as the data object;
- that is, the head is not merely a convenient shorthand for a
- structure. Similarly the
- X.I gn_next
- field is used to indicate whether or not the object has terminated.
- Unfortunately, if the object continues, the
- X.I gn_next
- field is also the address of where it continues. The link addresses
- carry no useful information when the object is serialized.
- LP
- The XDR data description of this linked list is described by the
- recursive declaration of
- X.I gnumbers_list :
- X.ie t .DS
- X.el .DS L
- X.ft CW
- struct gnumbers {
- int g_assets;
- int g_liabilities;
- };
- X.sp .5
- struct gnumbers_node {
- gnumbers gn_numbers;
- gnumbers_list gn_next;
- };
- X.sp .5
- union gnumbers_list switch (bool more_data) {
- case TRUE:
- gnumbers_node node;
- case FALSE:
- void;
- };
- X.DE
- X.LP
- In this description, the boolean indicates whether there is more data
- following it. If the boolean is
- X.I FALSE ,
- then it is the last data field of the structure. If it is
- X.I TRUE ,
- then it is followed by a gnumbers structure and (recursively) by a
- X.I gnumbers_list .
- Note that the C declaration has no boolean explicitly declared in it
- (though the
- X.I gn_next
- field implicitly carries the information), while the XDR data
- description has no pointer explicitly declared in it.
- X.LP
- Hints for writing the XDR routines for a
- X.I gnumbers_list
- follow easily from the XDR description above. Note how the primitive
- X.I xdr_pointer
- is used to implement the XDR union above.
- X.ie t .DS
- X.el .DS L
- X.ft CW
- bool_t
- xdr_gnumbers_node(xdrs, gn)
- XDR *xdrs;
- gnumbers_node *gn;
- {
- return(xdr_gnumbers(xdrs, &gn->gn_numbers) &&
- xdr_gnumbers_list(xdrs, &gp->gn_next));
- }
- X.sp .5
- bool_t
- xdr_gnumbers_list(xdrs, gnp)
- XDR *xdrs;
- gnumbers_list *gnp;
- {
- return(xdr_pointer(xdrs, gnp,
- sizeof(struct gnumbers_node),
- xdr_gnumbers_node));
- }
- X.DE
- X.LP
- The unfortunate side effect of XDR'ing a list with these routines
- is that the C stack grows linearly with respect to the number of
- node in the list. This is due to the recursion. The following
- routine collapses the above two mutually recursive into a single,
- non-recursive one.
- X.ie t .DS
- X.el .DS L
- X.ft CW
- bool_t
- xdr_gnumbers_list(xdrs, gnp)
- XDR *xdrs;
- gnumbers_list *gnp;
- {
- bool_t more_data;
- gnumbers_list *nextp;
- X.sp .5
- for (;;) {
- more_data = (*gnp != NULL);
- if (!xdr_bool(xdrs, &more_data)) {
- return(FALSE);
- }
- if (! more_data) {
- break;
- }
- if (xdrs->x_op == XDR_FREE) {
- nextp = &(*gnp)->gn_next;
- }
- if (!xdr_reference(xdrs, gnp,
- sizeof(struct gnumbers_node), xdr_gnumbers)) {
-
- return(FALSE);
- }
- gnp = (xdrs->x_op == XDR_FREE) ?
- nextp : &(*gnp)->gn_next;
- }
- *gnp = NULL;
- return(TRUE);
- }
- X.DE
- X.LP
- The first task is to find out whether there is more data or not,
- so that this boolean information can be serialized. Notice that
- this statement is unnecessary in the
- X.I XDR_DECODE
- case, since the value of more_data is not known until we
- deserialize it in the next statement.
- X.LP
- The next statement XDR's the more_data field of the XDR union.
- Then if there is truly no more data, we set this last pointer to
- X.I NULL
- to indicate the end of the list, and return
- X.I TRUE
- because we are done. Note that setting the pointer to
- X.I NULL
- is only important in the
- X.I XDR_DECODE
- case, since it is already
- X.I NULL
- in the
- X.I XDR_ENCODE
- and
- XDR_FREE
- cases.
- X.LP
- Next, if the direction is
- X.I XDR_FREE ,
- the value of
- X.I nextp
- is set to indicate the location of the next pointer in the list.
- We do this now because we need to dereference gnp to find the
- location of the next item in the list, and after the next
- statement the pointer
- X.I gnp
- will be freed up and no longer valid. We can't do this for all
- directions though, because in the
- X.I XDR_DECODE
- direction the value of
- X.I gnp
- won't be set until the next statement.
- X.LP
- Next, we XDR the data in the node using the primitive
- X.I xdr_reference .
- X.I xdr_reference
- is like
- X.I xdr_pointer
- which we used before, but it does not
- send over the boolean indicating whether there is more data.
- We use it instead of
- X.I xdr_pointer
- because we have already XDR'd this information ourselves. Notice
- that the xdr routine passed is not the same type as an element
- in the list. The routine passed is
- X.I xdr_gnumbers ,
- for XDR'ing gnumbers, but each element in the list is actually of
- type
- X.I gnumbers_node .
- We don't pass
- X.I xdr_gnumbers_node
- because it is recursive, and instead use
- X.I xdr_gnumbers
- which XDR's all of the non-recursive part. Note that this trick
- will work only if the
- X.I gn_numbers
- field is the first item in each element, so that their addresses
- are identical when passed to
- X.I xdr_reference .
- X.LP
- Finally, we update
- X.I gnp
- to point to the next item in the list. If the direction is
- X.I XDR_FREE ,
- we set it to the previously saved value, otherwise we can
- dereference
- X.I gnp
- to get the proper value. Though harder to understand than the
- recursive version, this non-recursive routine will never cause
- the C stack to blow up. It will also run more efficiently since
- a lot of procedure call overhead has been removed. Most lists
- are small though (in the hundreds of items or less) and the
- recursive version should be sufficient for them.
- Funky_Stuff
- cd ..
- echo more files to follow
- exit
-